package filereaders;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;


/**
 * 
 * @author Paul Dannenberg
 * 
 */

public class TxtParser extends Parser {

    /**
     * The column number, starting with 0 for the leftmost
     * column in the datafile to be read. If the .txt file
     * is reformatted with a different column ordering, simply
     * switch these Integers to reflect this.
     */
    private static final Integer DATE_COLUMN_NUMBER = 0;
    private static final Integer OPEN_COLUMN_NUMBER = 1;
    private static final Integer HIGH_COLUMN_NUMBER = 2;
    private static final Integer LOW_COLUMN_NUMBER = 3;
    private static final Integer CLOSE_COLUMN_NUMBER = 4;
    private static final Integer VOLUME_COLUMN_NUMBER = 5;
    private static final Integer CHANGE_COLUMN_NUMBER = 6;
    private static final Integer PERCENT_CHANGE_COLUMN_NUMBER = 7;
    private static final Integer ADJUSTED_CLOSING_COLUMN_NUMBER = 8;
    private static final int TITLE_ROW = 0;

    private Map<String, Integer> myColumns;

    /**
     * Creates a parser capable of reading .txt files, formatted in
     * by column as previously agreed
     * 
     * @param filename The name of the .txt file to read.
     */
    public TxtParser(String filename) {
        super(filename);
        myColumns = new HashMap<String, Integer>();
        setColumnNumbers();
    }

    private void setColumnNumbers() {
        myColumns.put("date", DATE_COLUMN_NUMBER);
        myColumns.put("open", OPEN_COLUMN_NUMBER);
        myColumns.put("high", HIGH_COLUMN_NUMBER);
        myColumns.put("low", LOW_COLUMN_NUMBER);
        myColumns.put("close", CLOSE_COLUMN_NUMBER);
        myColumns.put("volume", VOLUME_COLUMN_NUMBER);
        myColumns.put("change", CHANGE_COLUMN_NUMBER);
        myColumns.put("percent", PERCENT_CHANGE_COLUMN_NUMBER);
        myColumns.put("adj", ADJUSTED_CLOSING_COLUMN_NUMBER);
    }

    @Override
    public Collection<Double> parseField(String fieldName) {
        Scanner fileScanner = loadScanner();
        if (isIncorrectDataName(fieldName)) {
            return new ArrayList<Double>();
        }
        int columnToRead = myColumns.get(fieldName);
        return readColumn(fileScanner, columnToRead);
    }

    @Override
    public Map<Double, Double> parseRelatedFields(String keyField,
            String valueField) {
        Scanner fileKeyScanner = loadScanner();
        Scanner fileValueScanner = loadScanner();
        if (isIncorrectDataName(keyField) || isIncorrectDataName(valueField)) {
            return new HashMap<Double, Double>();
        }
        int keyColumnToRead = myColumns.get(keyField);
        int valueColumnToRead = myColumns.get(valueField);
        List<Double> keyColumn = readColumn(fileKeyScanner, keyColumnToRead);
        List<Double> valueColumn = readColumn(fileValueScanner,
                valueColumnToRead);
        return mergeData(keyColumn, valueColumn);
    }

    private boolean isIncorrectDataName(String dataName) {
        return (!myColumns.containsKey(dataName));
    }

    private List<Double> readColumn(Scanner fileScanner, int columnToRead) {
        List<Double> dataRead = new ArrayList<Double>();
        int rowNumber = 0;
        while (fileScanner.hasNextLine()) {
            Scanner lineScanner = new Scanner(fileScanner.nextLine());
            int columnIndex = 0;
            while (lineScanner.hasNext() && rowNumber != TITLE_ROW) {
                String dataPiece = lineScanner.next();
                if (columnToRead == columnIndex && !isWord(dataPiece)) {
                    double formattedDataPiece = formatDataToDouble(dataPiece);
                    dataRead.add(formattedDataPiece);
                }
                columnIndex++;
            }
            lineScanner.close();
            rowNumber++;
        }
        return dataRead;
    }

    /*
     * Given two lists, takes the i'th element of the first list makes
     * it a key in a map. Then takes the i'th element of the second
     * list and makes it this particular key's value.
     */
    private Map<Double, Double> mergeData(List<Double> keyColumn,
            List<Double> otherColumn) {
        Map<Double, Double> mergedData = new HashMap<Double, Double>();
        for (int i = 0; i < keyColumn.size(); i++) {
            mergedData.put(keyColumn.get(i), otherColumn.get(i));
        }
        return mergedData;
    }
}
